#include "public.hpp"
#include "json.hpp"
#include "user.hpp"
#include "group.hpp"
#include <iostream>
#include <thread>
#include <string>
#include <vector>
#include <chrono>
#include <ctime>
#include <unordered_map>
#include <functional>

#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

using json = nlohmann::json;

// 记录当前系统登录的用户信息
User currentUser;
// 记录当前登录用户的好友列表信息
std::vector<User> currentFriendList;
// 记录当前登录用户的群组列表信息
std::vector<Group> currentUserGroupList;
// 控制主菜单页面程序
bool isMainMenuRunning = false;

// 显示当前登录成功用户的基本信息
void ShowCurrentUserData();
// 接收线程
void ReadTaskHandler(int clientfd);
// 获取系统时间
std::string GetCurrentTime();
// 主聊天页面程序
void MainMenu(int);

// 实现聊天客户端,main线程用作发送线程,子线程用作接收线程
int main(int argc, char** argv)
{
    if (argc != 3) {
        std::cerr << "command invalid!\nexample: \n\t./ChatClient 127.0.0.1 6000" << std::endl;
        exit(-1); 
    }

    char* ip = argv[1];
    uint16_t port = atoi(argv[2]);

    // sock create
    int clientfd = socket(AF_INET, SOCK_STREAM, 0);
    if (clientfd < 0) {
        std::cerr << "socket create error!" << std::endl;
        exit(-1);
    }
    sockaddr_in server;
    memset(&server, 0, sizeof(sockaddr_in));

    // sock bind
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    inet_pton(AF_INET, ip, &(server.sin_addr));

    // sock connect
    if (connect(clientfd, (sockaddr*)&server, sizeof(sockaddr_in)) < 0) {
        std::cerr << "connect server error!" << std::endl;
        close(clientfd);
        exit(-1);
    }

    while (1) {
        std::cout << "=================================" << std::endl;
        std::cout << "             1. Login" << std::endl;
        std::cout << "             2. Register" << std::endl;
        std::cout << "             3. Quit" << std::endl;
        std::cout << "=================================" << std::endl;
        std::cout << "choice:";
        int choice = 0;
        std::cin >> choice;
        std::cin.get();  // 清除缓冲区的回车

        switch (choice) {
            case 1: {
                // Login
                int id = 0;
                char pwd[50] = {0};
                std::cout << "userid:";
                std::cin >> id;        
                std::cin.get();  // 清除缓冲区的回车
                std::cout << "password:";
                std::cin.getline(pwd, 50);

                json js;
                js["msgid"] = LOGIN_MSG;
                js["id"] = id;
                js["password"] = pwd;
                std::string request = std::move(js.dump());

                int len = send(clientfd, request.c_str(), strlen(request.c_str()) + 1, 0);
                if (len < 0) {
                    std::cerr << "send login msg error: " << request << std::endl; 
                }
                else {
                    char buffer[1024] = {0};
                    len = recv(clientfd, buffer, 1024, 0);
                    if (len < 0) {
                        std::cerr << "recv login response error" << std::endl;
                    }
                    else {
                        json rspJs = json::parse(buffer);
                        if (rspJs["errno"].get<int>() != 0) {
                            std::cerr << rspJs["errmsg"] << std::endl;
                        }
                        else {
                            // 登录成功
                            // 记录当前用户的id和name
                            currentUser.SetId(rspJs["id"].get<int>());
                            currentUser.SetName(rspJs["name"]);

                            // 记录当前用户的好友列表信息
                            if (rspJs.contains("friends")) {
                                // 初始化
                                currentFriendList.clear();
                                std::vector<std::string> firendsVec = rspJs["friends"];
                                for (std::string& friendsStr : firendsVec) {
                                    json friendJs = json::parse(friendsStr);
                                    User user;
                                    user.SetId(friendJs["id"].get<int>());
                                    user.SetName(friendJs["name"]);
                                    user.SetState(friendJs["state"]);
                                    currentFriendList.push_back(user);
                                }
                            }

                            // 记录当前用户的群组列表信息
                            if (rspJs.contains("groups")) {
                                // 初始化
                                currentUserGroupList.clear();
                                std::vector<std::string> groupsVec = rspJs["groups"];
                                for (std::string& groupStr : groupsVec) {
                                    json groupsJs = json::parse(groupStr);
                                    Group group;
                                    group.SetId(groupsJs["id"].get<int>());
                                    group.SetName(groupsJs["groupname"]);
                                    group.SetDesc(groupsJs["groupdesc"]);

                                    std::vector<std::string> usersVec = groupsJs["users"];
                                    for (std::string& userStr : usersVec) {
                                        GroupUser user;
                                        json userJs = json::parse(userStr);
                                        user.SetId(userJs["id"].get<int>());
                                        user.SetName(userJs["name"]);
                                        user.SetState(userJs["state"]);
                                        user.SetRole(userJs["role"]);
                                        group.GetUsers().push_back(user);
                                    }

                                    currentUserGroupList.push_back(group);
                                }
                            }

                            // 显示登录用户的基本信息
                            ShowCurrentUserData();

                            // 显示当前用户的离线消息  个人聊天信息或者群组消息
                            if (rspJs.contains("offlinemsg")) {
                                std::vector<std::string> msgVec = rspJs["offlinemsg"];
                                for (std::string& msgStr : msgVec) {
                                    json msgJs = json::parse(msgStr);
                                    // time + id + name + " said: " + "xxx"
                                    if (msgJs["msgid"].get<int>() == ONE_CHAT_MSG) {
                                        // 私聊消息
                                        std::cout << msgJs["time"].get<std::string>() << " [" << msgJs["id"] <<
                                                     "]" << msgJs["name"].get<std::string>() << " said: " << 
                                                     msgJs["msg"].get<std::string>() << std::endl;
                                    }
                                    else {
                                        // 群消息
                                        std::cout << "群消息[" << msgJs["groupid"] << "]:" << msgJs["time"].get<std::string>() <<
                                                     " [" << msgJs["id"] << "]" << msgJs["name"].get<std::string>() <<
                                                     " said: " << msgJs["msg"].get<std::string>() << std::endl;
                                    }
                                }
                            }

                            // 登录成功, 启动接收线程负责接收数据, 该线程只启动一次
                            static int readHreadNumber = 0;
                            if (readHreadNumber == 0) {
                                std::thread readTask(ReadTaskHandler, clientfd);
                                readTask.detach();  // 线程分离
                                readHreadNumber++;
                            }

                            // 进入聊天主菜单页面
                            isMainMenuRunning = true;
                            MainMenu(clientfd);

                        }
                    }
                }
            }
            break;
            case 2: {
                // Register
                char name[50] = {0};
                char pwd[50] = {0};
                std::cout << "username:";
                std::cin.getline(name, 50);
                std::cout << "userpassword:";
                std::cin.getline(pwd, 50);

                json js;
                js["msgid"] = REG_MSG;
                js["name"] = name;
                js["password"] = pwd;
                std::string request = std::move(js.dump());

                int len = send(clientfd, request.c_str(), strlen(request.c_str()) + 1, 0);
                if (len < 0) {
                    std::cerr << "send reg msg error:" << request << std::endl;
                }
                else {
                    char buffer[1024] = {0};
                    len = recv(clientfd, buffer, 1024, 0);
                    if (len < 0) {
                        std::cerr << "recv msg response error" << std::endl;
                    }
                    else {
                        json msgJs = json::parse(buffer);
                        if (msgJs["errno"].get<int>() != 0) {
                            std::cerr << name << " is already exist, register error!" << std::endl;
                        }
                        else {
                            // 注册成功
                            std::cout << name << " register success, userid is " << msgJs["id"] << ", do not forget it!" << std::endl;
                        }
                    }
                }
            }
            break;
            case 3: {
                // Quit
                close(clientfd);
                exit(0);
            }
            break;
            default: {
                std::cerr << "invalid input!" << std::endl;
                break;
            }
        }
    }
    return 0;
}

// 接收线程
void ReadTaskHandler(int clientfd)
{
    while (1) {
        char buffer[1024] = {0};
        int len = recv(clientfd, buffer, 1024, 0);
        if (len <= 0) {
            close(clientfd);
            exit(-1);
        } 

        // 接收ChatService转发的数据, 反序列化生成json数据对象
        json js = json::parse(buffer);
        int msgType = js["msgid"].get<int>();
        if (msgType == ONE_CHAT_MSG) {
            // 私聊消息
            std::cout << js["time"].get<std::string>() << "[ " << js["id"] << "]" << 
                         js["name"].get<std::string>() << " said: " << 
                         js["msg"].get<std::string>() << std::endl;
            continue;
        }

        if (msgType == GROUP_CHAT_MSG) {
            // 群消息
            std::cout << "群消息[" << js["groupid"] << "]:" << js["time"].get<std::string>() << 
                         " [" << js["id"] << "]" << js["name"].get<std::string>() << 
                         " said: " << js["msg"].get<std::string>() << std::endl;
            continue;
        }
    }
}

// 显示当前登录成功用户的基本信息
void ShowCurrentUserData()
{
    std::cout << "======================login user======================" << std::endl;
    std::cout << "current login user => id:" << currentUser.GetId() << " name:" << currentUser.GetName() << std::endl;
    std::cout << "----------------------friend list---------------------" << std::endl;
    if (!currentFriendList.empty()) {
        for (User& user : currentFriendList) {
            std::cout << user.GetId() << " " << user.GetName() << " " << user.GetState() << std::endl;
        }
    }

    std::cout << "----------------------group list----------------------" << std::endl;
    if (!currentUserGroupList.empty()) {
        for (Group& group : currentUserGroupList) {
            std::cout << group.GetId() << " " << group.GetName() << " " << group.GetDesc() << std::endl;
            for (GroupUser& user : group.GetUsers()) {
                std::cout << user.GetId() << " " << user.GetName() << " " << 
                user.GetState() << " " << user.GetRole() << std::endl;
            }
        }
    }
    std::cout << "======================================================" << std::endl;
}

// "help" command handler
void Help(int fd = 0, std::string str = "");
// "chat" command handler
void Chat(int, std::string);
// "addfriend" command handler
void AddFriend(int, std::string);
// "creategroup" command handler
void CreateGroup(int, std::string);
// "addgroup" command handler
void AddGroup(int, std::string);
// "groupchat" command handler
void GroupChat(int, std::string);
// "loginout" command handler
void LoginOut(int, std::string);

// 系统支持的客户端命令列表
std::unordered_map<std::string, std::string> commandMap = {
    {"help", "显示所有支持的命令,格式help"},
    {"chat", "一对一聊天,格式chat:friendid:message"},
    {"addfriend", "添加好友,格式addfriend:friendid"},
    {"creategroup", "创建群组,格式creategroup:groupname:groupdesc"},
    {"addgroup", "加入群组,格式addgroup:groupid"},
    {"groupchat", "群聊,格式groupchat:groupid:message"},
    {"loginout", "注销,格式loginout"}
};

// 注册系统支持的客户端命令处理
std::unordered_map<std::string, std::function<void(int, std::string)>> commandHandlerMap = {
    {"help", Help},
    {"chat", Chat},
    {"addfriend", AddFriend},
    {"creategroup", CreateGroup},
    {"addgroup", AddGroup},
    {"groupchat", GroupChat},
    {"loginout", LoginOut}
};

// 主聊天页面程序
void MainMenu(int clientfd)
{
    Help();

    char buffer[1024] = {0};
    while (isMainMenuRunning) {
        std::cin.getline(buffer, 1024);
        std::string commandbuf(buffer);
        std::string command;  // 存储命令
        int idx = commandbuf.find(":");
        if (idx < 0) {
            command = commandbuf;
        }
        else {
            command = commandbuf.substr(0, idx);
        }
        auto it = commandHandlerMap.find(command);
        if (it == commandHandlerMap.end()) {
            std::cerr << "invalid input command!" << std::endl;
            continue;
        }

        // 调用相应命令的事件处理回调,MainMenu对修改封闭,添加新功能不需要修改该函数
        it->second(clientfd, commandbuf.substr(idx + 1, commandbuf.size() - idx));
    }
}

void Help(int clientfd, std::string str)
{
    std::cout << "show command list >>> " << std::endl;
    for (auto& p : commandMap) {
        std::cout << p.first << " : " << p.second << std::endl;
    }
    std::cout << std::endl;
}

void AddFriend(int clientfd, std::string str)
{
    int firendid = atoi(str.c_str());
    json js;
    js["msgid"] = ADD_FRIEND_MSG;
    js["id"] = currentUser.GetId();
    js["firendid"] = firendid;
    std::string buffer = std::move(js.dump());

    int len = send(clientfd, buffer.c_str(), strlen(buffer.c_str()) + 1, 0);
    if (len < 0) {
        std::cerr << "send addfirend msg error -> " << buffer << std::endl;
    }
}

void Chat(int clientfd, std::string str)
{
    int idx = str.find(":");  // friendid:message
    if (idx < 0) {
        std::cerr << "chat command invalid!" << std::endl;
        return;
    }

    int friendid = atoi(str.substr(0, idx).c_str());
    std::string message = str.substr(idx + 1, str.size() - idx);

    json js;
    js["msgid"] = ONE_CHAT_MSG;
    js["id"] = currentUser.GetId();
    js["name"] = currentUser.GetName();
    js["toid"] = friendid;
    js["msg"] = message;
    js["time"] = GetCurrentTime();
    std::string buffer = std::move(js.dump());

    int len = send(clientfd, buffer.c_str(), strlen(buffer.c_str()) + 1, 0);
    if (len < 0) {
        std::cerr << "send chat msg error -> " << buffer << std::endl; 
    }
}

void CreateGroup(int clientfd, std::string str) 
{
    int idx = str.find(":");
    if (idx < 0) {
        std::cerr << "creategroup command invalid!" << std::endl;
        return;
    }

    std::string groupname = str.substr(0, idx);
    std::string groupdesc = str.substr(idx + 1, str.size() - idx);
    
    json js;
    js["msgid"] = CREATE_GROUP_MSG;
    js["id"] = currentUser.GetId();
    js["groupname"] = groupname;
    js["groupdesc"] = groupdesc;
    std::string buffer = std::move(js.dump());

    int len = send(clientfd, buffer.c_str(), strlen(buffer.c_str()) + 1, 0);
    if (len < 0) {
        std::cerr << "send addgroup msg error -> " << buffer << std::endl;
    }
}

void AddGroup(int clientfd, std::string str)
{
    int groupid = atoi(str.c_str());
    json js;
    js["msgid"] = ADD_GROUP_MSG;
    js["id"] = currentUser.GetId();
    js["groupid"] = groupid;
    std::string buffer = std::move(js.dump());

    int len = send(clientfd, buffer.c_str(), strlen(buffer.c_str()) + 1, 0);
    if (len < 0) {
        std::cerr << "send addgroup msg error -> " << buffer << std::endl;
    }
}

void GroupChat(int clientfd, std::string str)
{
    int idx = str.find(":");
    if (idx < 0) {
        std::cerr << "groupchat command invalid!" << std::endl;
        return;
    }

    int groupid = atoi(str.substr(0, idx).c_str());
    std::string message = str.substr(idx + 1, str.size() - idx);

    json js;
    js["msgid"] = GROUP_CHAT_MSG;
    js["id"] = currentUser.GetId();
    js["name"] = currentUser.GetName();
    js["groupid"] = groupid;
    js["msg"] = message;
    js["time"] = GetCurrentTime();
    std::string buffer = std::move(js.dump());

    int len = send(clientfd, buffer.c_str(), strlen(buffer.c_str()) + 1, 0);
    if (len < 0) {
        std::cerr << "send groupchat msg error -> " << buffer << std::endl;
    }
}

void LoginOut(int clientfd, std::string)
{
    json js;
    js["msgid"] = LOGIN_MSG;
    js["id"] = currentUser.GetId();
    std::string buffer = std::move(js.dump());

    int len = send(clientfd, buffer.c_str(), strlen(buffer.c_str()) + 1, 0);
    if (len < 0) {
        std::cerr << "send loginout msg error -> " << buffer << std::endl;
    }
    else {
        isMainMenuRunning = false;
    }
}

std::string GetCurrentTime()
{
    auto tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    struct tm* ptm = localtime(&tt);
    char date[60] = {0};
    sprintf(date, "%d-%02d-%02d %02d:%02d:%02d",
            (int)ptm->tm_year + 1900, (int)ptm->tm_mon + 1, (int)ptm->tm_mday,
            (int)ptm->tm_hour, (int)ptm->tm_min, (int)ptm->tm_sec);
    return std::string(date);  
}